class Upload < ActiveRecord::Base
  attr_accessible :title, :description, :final, :deadline_id
  # Relations
  belongs_to  :user
  belongs_to  :deadline
  belongs_to  :group
  
  # Validations
  validates_presence_of :title, :file_name, :size, :path, :final, :group_id, :user_id
  validates_uniqueness_of :path
  validates_not_zero      :group_id, :user_id, :deadline_id
  # Before destroy files should be deleted
  #
  def before_destroy
    delete_files
  end
  
  # Deletes files associated with upload
  #
  def delete_files
    unless self.path.blank?
      Upload.delete_file(self.path)
    end
  end
  
  # Returns the URL to the file
  #
  def url
    "#{AppConfig.uploads_prepend || ''}#{self.path}"
  end
  
  # Accessor to set the file for self.
  # Takes the path to the temp file. File is deleted after processing.
  # If the file was correctly processed, it will return a hash of its meta data.
  #
  def file=(file_path)
    if File.exists?(file_path) # Make sure file exists
      self.size = File.size(file_path)
      prepend = "#{Digest::SHA256.hexdigest(file_path + Time.now.to_s + rand(211).to_s)[0,10]}/"
      self.path = Upload.generate_filename(self.file_name, false, prepend)
      
      # Don't allow to files to have the same path
      while Upload.file_exists?(self.path)
        prepend = "#{Digest::SHA256.hexdigest(file_path + Time.now.to_s + rand(211).to_s)[0,10]}/"
        self.path = Upload.generate_filename(self.file_name, false, prepend)
      end
      
      logger.info(file_path)
      if Upload.save_file(file_path, self.path)
        return true
      else # Unable to store file
        File.delete(file_path)
        self.errors.add(:file_name, 'unable to process file')
        return false
      end
    else # Doesn't exist
      self.errors.add(:file_name, 'can\'t access file')
      return false
    end
  end
  
  # Saves temp file file_path as file_name
  #
  def self.save_file(file_path, file_name, delete = true)
    save_to = File.join(AppConfig.uploads_base, file_name)
    unless(save_to == file_path)
      # If directory doesn't exist, create it
      if(!File.directory?(File.dirname(save_to)))
        FileUtils.mkdir_p File.dirname(save_to)
      end
      File.delete(save_to) if(File.exists?(save_to))
      (delete ? FileUtils.mv(file_path, save_to) : FileUtils.cp(file_path, save_to))
    end
    return true
  end
  
  # Deletes file_name
  #
  def self.delete_file(file_name)
    to_delete = File.join(AppConfig.uploads_base, file_name)
    File.delete(to_delete) if(File.exists?(to_delete))
    return true
  end
  
  # Checks if file_name exists
  #
  def self.file_exists?(file_name)
    file_name = File.join(AppConfig.uploads_base, file_name)
    return File.exists?(file_name)
  end
  
  # Sanitize a file name.
  #
  def self.get_base_name(file_name)
    # get only the filename, not the whole path (IE fix)
    just_filename = File.basename(file_name).strip
    # replace all non-alphanumeric, underscore or periods with underscores
    just_filename.gsub(/[^\w\.\-]+/,'-')
  end
  
  # Generate a random (only if random is true) string
  # to be used for file names.
  # file_name: string to turn into file name
  # random: flag used to know wether a random file name should be used
  # prepend: string to be prepended before the file name
  # format: extension to be used; if not set uses original file's extension
  #
  def self.generate_filename(file_name, random = false, prepend = nil, format = nil)
    if random
      file_name = "#{prepend ? prepend.to_s : ''}" + Digest::SHA256.hexdigest(Time.now.to_s + file_name + DateTime.now.to_s)[1,10] + "_" + Digest::SHA256.hexdigest(file_name + Time.now.to_s + DateTime.now.to_s + file_name) + (format ? ".#{format}" : File.extname(file_name))
    else
      file_name = "#{prepend ? prepend.to_s : ''}" + self.get_base_name(file_name).strip.gsub(/\s+/, '-').gsub(/[^a-zA-Z0-9\-\.\_]/, '') + (format ? ".#{format}" : '')
    end
    return file_name
  end
end
